using System;
using System.Collections.Generic;
using System.Text;
using JobShopModel.Model.FactoryLvl;
using JobShopModel.Model.JobLvl;
//TODO DOCUMENTATION
//TODO RESOLVE THE STATE PROBLEM:
namespace JobShopModel.Model.Station
{
    /// <summary>
    /// The stations that consist of a set of machines and a queue of waiting jobs
    /// (i.e. stations that are not the IOStation)
    /// </summary>
    class RegularStation:IStations
    {

        private Queue<Job> jobQueue;

       // private StationsStates states;

        private StationsName name;

        private List<Machine> machineList;

        private Time totalTimeIddle;
        private Time totalTimeBlocked;
        private Time totalTimeOperating;
        private Time totalWaitingQueue;

        private int jobCounter;

        private Decimal avgTimeIddle;
        private Decimal avgTimeBlocked;
        private Decimal avtTimeOperating;
        private Time avgWaitingQueue;

 

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">Which station this is</param>
        /// <param name="numMachine">How many machines it has</param>
        public RegularStation(StationsName name,int numMachine)
        {
            //this.states = StationsStates.AVAILABLE;

            this.name = name;

            this.jobQueue = new Queue<Job>();

            //Set all the machine of the station
            this.machineList = new List<Machine>();
            for (int i = 0; i < numMachine; i++)
            {
                machineList.Add(new Machine());
            }

        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">Which station this is</param>
        /// <param name="numMachine">How many machines it has</param>
        /// <param name="initTime">What time it was initialized</param>
        public RegularStation(StationsName name, int numMachine, Time initTime)
        {

            this.name = name;

            this.jobQueue = new Queue<Job>();

            this.totalTimeBlocked = new Time();
            this.totalTimeIddle = new Time();
            this.totalTimeOperating = new Time();
            this.avgTimeBlocked = new Decimal();
            this.avgTimeIddle = new Decimal();
            this.avtTimeOperating = new Decimal();
            this.totalWaitingQueue = new Time();
            this.avgWaitingQueue = new Time();

            this.jobCounter = 0;


            //Set all the machine of the station
            this.machineList = new List<Machine>();
            for (int i = 0; i < numMachine; i++)
            {
                machineList.Add(new Machine(initTime));
            }

        }


        public Time TotalWaitingQueue
        {
            get { return totalWaitingQueue; }
            set { totalWaitingQueue = value; }
        }

        public Time AvgWaitingQueue
        {
            get { return avgWaitingQueue; }
            set { avgWaitingQueue = value; }
        }

        public Time TotalOperationTime
        {
            get { return this.totalTimeOperating; }
            set 
            {
                Time total = (this.totalTimeOperating * machineList.Count) + value;
                this.totalTimeOperating = total / machineList.Count;
            }
        }

        public Time TotalIddleTime
        {
            get { return this.totalTimeIddle; }
            set 
            {
                Time total = (this.totalTimeIddle * machineList.Count) + value;
                this.totalTimeIddle = total/machineList.Count; 
            }
        }

        public Time TotalBlockedTime
        {
            get { return this.totalTimeBlocked; }
            set 
            { 
                Time total = (this.totalTimeBlocked * machineList.Count) + value;
                this.totalTimeBlocked = total / machineList.Count;
            }
        }

        public int JobCounter
        {
            get { return (this.jobCounter - jobQueue.Count); }
        }

        public int QueueCounter
        {
            get { return this.jobQueue.Count; }
        }

        public Decimal AvgBlockedTime
        {
            get { return this.avgTimeBlocked; }
            set { this.avgTimeBlocked = value; }

        }

        public Decimal AvgIddleTime
        {
            get { return this.avgTimeIddle; }
            set { this.avgTimeIddle = value; }
        }

        public Decimal AvgOperatingTime
        {
            get { return this.avtTimeOperating; }
            set { this.avtTimeOperating = value; }
        }



        public StationsName Name
        {
            get { return this.name; }
        }

        /// <summary>
        /// Return state depending on the state if the Machines
        /// It can be availlable or buzy...
        /// </summary>
        public StationsStates State
        {
            get 
            {
                bool rtn = false;
                //Search for an iddle machine
                foreach (Machine m in machineList)
                {
                    if (m.State == MachineStates.IDDLE)
                    {
                        rtn = true;
                        break;
                    }
                }
                if (rtn)
                {
                    return StationsStates.AVAILABLE;
                }
                else
                {
                    return StationsStates.BUSY;
                }

            }
           

        }

        /// <summary>
        /// Since the machines are not distinct, this method goes through the station's machines
        /// and returns the first idle machine
        /// </summary>
        /// <returns>Instance of the first machine found that is not working or blocked</returns>
        public Machine getIddleMachine()
        {
            Machine rtn = null;
            foreach (Machine machine in machineList)
            {
                if( machine.State == MachineStates.IDDLE)
                {
                    rtn = machine;
                    break;
                }
            }
            return rtn;
        }

        /// <summary>
        /// Since the machines are not distinct, this method finds the machine that an operation is at
        /// </summary>
        /// <param name="op">Which operation we are finding</param>
        /// <returns>Instance of the machine it is at</returns>
        public Machine getMachine(Operation op)
        {
            Machine rtn = null;
            int i = 0;
            foreach (Machine m in machineList)
            {
                if (m.CurrentOperation == op)
                {
                    rtn = m;
                    i++;
                }
            }
            if (i > 1)
            {
                throw new Exception("Error: More that one machine got ");
            }
            return rtn;
        }


        /// <summary>
        /// Add a job to the Queue list.
        /// </summary>
        /// <param name="job"></param>
        public void EnqueueJob(Job job)
        {
            job.Position = this.Name;
            jobCounter++;
            jobQueue.Enqueue(job);
        }

        /// <summary>
        /// Check if the Queue is empty
        /// </summary>
        /// <returns>true if empty/false if not</returns>
        public bool isJobQueued()
        {
            return (jobQueue.Count != 0);
        }

        /// <summary>
        /// Get the first element of the Queue list
        /// </summary>
        /// <returns>Job to be used.</returns>
        public Job DequeueJob()
        {
            return jobQueue.Dequeue();
        }

    }
}
